package com.woniu.user.service.impl;

import cn.hutool.core.lang.Snowflake;
import cn.hutool.core.util.RandomUtil;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniu.user.config.AlipayConfig;
import com.woniu.user.controller.form.CoachApplyForm;
import com.woniu.user.controller.form.ModifySelfForm;
import com.woniu.user.controller.form.ModifyPwdForm;
import com.woniu.user.controller.form.RegisterForm;
import com.woniu.user.controller.form.*;
import com.woniu.user.dao.CoachDao;
import com.woniu.user.exception.WebException;
import com.woniu.user.controller.form.LoginForm;
import com.woniu.user.exception.WebExceptionCode;
import com.woniu.user.model.Account;
import com.woniu.user.dao.AccountDao;
import com.woniu.user.model.Charge;
import com.woniu.user.model.Coach;
import com.woniu.user.service.AccountService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.woniu.user.service.ChargeService;
import com.woniu.user.service.dto.AccountDto;
import com.woniu.user.service.CoachApplyService;
import com.woniu.user.service.dto.CoachInfoDto;
import com.woniu.user.util.BeanCopyUtil;
import com.woniu.user.util.JwtUtil;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.mail.javamail.JavaMailSender;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.mail.Message;
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.nio.charset.StandardCharsets;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * 用户账号表（学员和教练） 服务实现类
 * </p>
 *
 * @author Silas
 * @since 2023-02-15
 */
@Service
@Slf4j
public class AccountServiceImpl extends ServiceImpl<AccountDao, Account> implements AccountService {

    @Resource
    private CoachDao coachDao;
    @Resource
    private JavaMailSender javaMailSender;
    @Resource
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private CoachApplyService coachApplyService;
    @Autowired
    private StringRedisTemplate redisTemplate;
    @Autowired
    private ChargeService chargeService;
    @Resource
    private RocketMQTemplate mqTemplate;

    @Value("${aliOSS.EndPoint}")
    public String endPoint;// 地域节点
    @Value("${aliOSS.BucketURL}")
    public String bucketURL;// bucket公网域名
    @Value("${aliOSS.BucketName}")
    public String bucketName;// bucket名称
    @Value("${aliOSS.AccessKeyID}")
    public String accessKeyID;
    @Value("${aliOSS.AccessKeySecret}")
    public String accessKeySecret;

    @Override
    public Cookie login(LoginForm loginForm) {
        Account account = getOne(new QueryWrapper<Account>().eq("email", loginForm.getUsername()));
        //1.判断用户名是否存在？
        if (ObjectUtils.isEmpty(account)) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        //2.判断用户是否可用？
        if ("不可用".equals(account.getState())) throw new WebException(WebExceptionCode.USER_IS_NOT_AVAILABLE);
        //3.判断密码是否正确
        if (!account.getPassword().equals(loginForm.getPassword()))
            throw new WebException(WebExceptionCode.PASSWORD_IS_NOT_RIGHT);
        //4.发放令牌
        Map<String, Object> map = new HashMap<>();
        map.put("id", account.getAccountId());
        map.put("type", account.getType());
        String token = JwtUtil.getToken(map, 60);// 60分钟过期
        Cookie cookie = new Cookie("token", token);
        // cookie.setMaxAge(30 * 60 * 60);// 有效期30分钟
        return cookie;
    }

    @Override
    public void register(RegisterForm registerForm) throws MessagingException {
        Account newAccount = BeanCopyUtil.copyObject(registerForm, Account.class);
        newAccount.setState("不可用");
        Account account = this.getOne(new QueryWrapper<Account>().eq("email", newAccount.getEmail()));
        if (account != null) throw new WebException(WebExceptionCode.EMAIL_IS_EXISTS);
        newAccount.setType("学员");
        newAccount.setBalance(BigDecimal.valueOf(0));
        newAccount.setImage("default.png");
        save(newAccount);
        Account one = getOne(new QueryWrapper<Account>().eq("email", newAccount.getEmail()));
        Integer accountId = one.getAccountId();

        String title = "用户激活";
        String content = "<a href='http://192.168.10.39:8080/account/active/" + accountId + "'>点击激活</a>";
        String addr = registerForm.getEmail();
        String[] param = {addr, title, content};

        mqTemplate.syncSend("register-topic", param);// 参数为tag与消息体

        //检测是否注册成功
        org.springframework.messaging.Message<String> build = MessageBuilder.withPayload(registerForm.getEmail()).build();
        SendResult sendResult = rocketMQTemplate.syncSend("register-check-topic", build, 30000L, 14);
        log.info("注册申请:{}", sendResult);

    }

    @Override
    public void closeRegister(String email) {
        Account user = getOne(new QueryWrapper<Account>().eq("email", email));
        if (Objects.isNull(user)) {
            log.warn("账户不存在", user);

            return;
        }
        if (user.getState().equals("不可用")) {
            log.info("注册取消:{}", user);
            removeById(user);

        } else {
            //若成功则发送欢迎邮件,否则移除该账号
            log.info("注册结果消息:{}", "success");
        }
    }

    @Override
    public void active(Integer id) {
        //点击邮件中的激活会调用这个
        Account accountDb = getById(id);
        accountDb.setState("可用");
        updateById(accountDb);
    }

    @Override
    public void beCoach(int accountFromToken, CoachApplyForm coachApplyForm) {
        Account account = getById(accountFromToken);
        //教练资格，点击就送
        coachApplyService.beCoach(coachApplyForm, account);

    }

    @Override
    public void modifySelf(Integer userId, ModifySelfForm form) {
        Account account = getById(userId);
        if (account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        if (!"可用".equals(account.getState())) throw new WebException(WebExceptionCode.USER_IS_NOT_AVAILABLE);
        String rawUsername = form.getUsername();// 要修改的用户名
        String username = account.getUsername();// 原用户名
        if (rawUsername != null && !rawUsername.equals(username)) {
            // 如果要修改用户名 需要检查是否有重名
            Account user = this.getOne(new QueryWrapper<Account>().eq("username", rawUsername));
            if (user != null) throw new WebException(WebExceptionCode.USERNAME_EXISTS);
        }
        account.setUsername(rawUsername);
        account.setNickname(form.getNickname());
        account.setEmail(form.getEmail());
        account.setPhone(form.getPhone());
        account.setGender(form.getGender());
        account.setAge(form.getAge());
        updateById(account);
    }

    @Override
    public void modifyCoach(Integer userId, ModifyCoachForm modifyCoachForm) {
        Account account = getById(userId);
        account.setNickname(modifyCoachForm.getNickname());
        account.setEmail(modifyCoachForm.getEmail());
        account.setPhone(modifyCoachForm.getPhone());
        account.setGender(modifyCoachForm.getGender());
        account.setAge(modifyCoachForm.getAge());
        updateById(account);
        Coach coach = coachDao.selectOne(new QueryWrapper<Coach>().eq("account_id", userId));
        coach.setIntroduce(modifyCoachForm.getIntroduce());
        coach.setStyle(modifyCoachForm.getStyle());
        coach.setPrice(modifyCoachForm.getPrice());
        coachDao.updateById(coach);
    }

    @Override
    public CoachInfoDto getCoachInfo(Integer userId) {
        Account account = getById(userId);
        CoachInfoDto coachInfoDto = BeanCopyUtil.copyObject(account, CoachInfoDto.class);
        Coach coach = coachDao.selectOne(new QueryWrapper<Coach>().eq("account_id", userId));
        coachInfoDto.setCoachId(coach.getCoachId());
        coachInfoDto.setRealname(coach.getRealname());
        coachInfoDto.setVenueName(coach.getVenueName());
        coachInfoDto.setIntroduce(coach.getIntroduce());
        coachInfoDto.setStyle(coach.getStyle());
        coachInfoDto.setPrice(coach.getPrice());
        return coachInfoDto;
    }

    // 发送验证码找回
    @SneakyThrows
    @Override
    public void sendCode(String rawEmail) {
        Account account = this.getOne(new QueryWrapper<Account>().eq("email", rawEmail));
        if (account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        if (!rawEmail.equals(account.getEmail())) throw new WebException(WebExceptionCode.EMAIL_NOT_MATCH);
        String code = RandomUtil.randomNumbers(6);
        String title = "验证码";
        String content = "您的验证码是：" + code + "十分钟有效";
        String[] param = {rawEmail, title, content};
        SendResult sendResult = mqTemplate.syncSend("code-topic", param);// 参数为tag与消息体
        log.info(sendResult.toString());// 输出发送消息的返回结果
        this.saveCode(rawEmail, code);
    }

    // 修改密码
    @Override
    public void changePwd(Integer userId, ModifyPwdForm form) {
        Account account = this.getById(userId);
        if (account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        String password = account.getPassword();// 获取用户密码
        if (!form.getOldPassword().equals(password)) throw new WebException(WebExceptionCode.PASSWORD_IS_NOT_RIGHT);
        account.setPassword(form.getNewPassword());
        this.updateById(account);
    }

    // 获取学员所有信息
    @Override
    public AccountDto getUserInfo(Integer userId) {
        Account account = this.getById(userId);
        if (account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        return BeanCopyUtil.copyObject(account, AccountDto.class);
    }

    // 修改头像
    @SneakyThrows
    @Override
    public String updateProfile(MultipartFile file, Integer userId) {
        Account account = this.getById(userId);
        if (account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        String rawName = file.getOriginalFilename();// 获取原始文件名称
        OSS ossClient = new OSSClientBuilder().build(endPoint, accessKeyID, accessKeySecret);// 创建OSS连接服务对象
        ossClient.putObject(bucketName, rawName, file.getInputStream());// 向bucketName这个桶容器存入名为rawName的文件（流形式上传）
        String fileURL = bucketURL + rawName;// 文件的url
        account.setImage(rawName);
        this.updateById(account);// 更新数据库中的账户信息
        return fileURL;
    }

    // 发送邮件
    @Override
    public void sendMail(String addr, String title, String content) throws MessagingException {
        MimeMessage message = javaMailSender.createMimeMessage();
        message.setFrom(new InternetAddress("xuxilin1997@163.com"));//设置发送者
        message.setRecipient(Message.RecipientType.TO, new InternetAddress(addr));//设置接收者
        message.setContent(content, "text/html;charset=utf-8");//设置邮件内容
        message.setSubject(title);
        javaMailSender.send(message);
        log.info("发送了邮件{} {} {}", addr, title, content);
    }

    // 收到验证码并验证
    @Override
    public void checkCode(String email, String rawCode) {
        if (email == null || rawCode == null) throw new WebException(WebExceptionCode.PARAM_NULL);
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        String code = operations.get(email);
        if (code == null || !code.equals(rawCode)) throw new WebException(WebExceptionCode.CODE_INCORRECT);
    }

    // 重置密码
    @Override
    public void resetPwd(RegisterForm registerForm) {
        String email = registerForm.getEmail();
        String password = registerForm.getPassword();
        if (email == null || password == null) throw new WebException(WebExceptionCode.PARAM_NULL);
        Account account = this.getOne(new QueryWrapper<Account>().eq("email", email));
        if (account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        account.setPassword(password);
        this.updateById(account);
    }

    // 检查余额
    @Override
    public void checkBalance(Integer userId, BigDecimal money) {
        Account byId = getById(userId);
        if (money.compareTo(byId.getBalance()) < 0) {
            throw new WebException(WebExceptionCode.BALANCE_IS_NOT_ENOUGH);
        }
    }

    // 根据用户id获取邮箱
    @Override
    public String getUserEmailById(Integer userId) {
        Account user = this.getById(userId);
        if(user == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        return user.getEmail();
    }

    // 提现
    @Override
    public void cashOut(Integer userId, CashOutForm form) {
        Account account = this.getById(userId);
        if(account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        if(!account.getPassword().equals(form.getPassword())) throw new WebException(WebExceptionCode.PASSWORD_IS_NOT_RIGHT);
        BigDecimal balance = account.getBalance();
        BigDecimal money = form.getMoney();
        if(money.compareTo(balance) == 1) throw new WebException(WebExceptionCode.CASH_MONEY_INCORRECT);
        if(money.compareTo(BigDecimal.ZERO) != 1) throw new WebException(WebExceptionCode.CASH_NOT_BIGGER_THAN_ZERO);
        BigDecimal result = balance.subtract(money);
        account.setBalance(result);
        this.updateById(account);
    }

    // 充值
    @SneakyThrows
    @Override
    public String cashIn(Integer userId, BigDecimal money) {
        // 用户校验
        Account account = this.getById(userId);
        if(account == null) throw new WebException(WebExceptionCode.USER_IS_NOT_EXISTS);
        // 参数校验
        if(money.compareTo(BigDecimal.ZERO)!=1) throw new WebException(WebExceptionCode.CASH_NOT_BIGGER_THAN_ZERO);

        // 创建支付宝接口连接对象
        AlipayClient alipayClient = new DefaultAlipayClient(
                AlipayConfig.gatewayUrl,
                AlipayConfig.appId,
                AlipayConfig.merchantPrivateKey,
                AlipayConfig.format,
                AlipayConfig.charset,
                AlipayConfig.alipayPublicKey,
                AlipayConfig.signType);

        // 创建支付宝交易页面请求对象
        AlipayTradePagePayRequest alipayRequest = new AlipayTradePagePayRequest();
        alipayRequest.setReturnUrl(AlipayConfig.returnUrl);
        alipayRequest.setNotifyUrl(AlipayConfig.notifyUrl);

        // 准备支付页面信息
        String no = new Snowflake(1, 1).nextIdStr();// 生成随机单号
        String outTradeNo = new String(no.getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);// 订单号 必填
        String totalAmount = new String(money.toString().getBytes(StandardCharsets.ISO_8859_1), StandardCharsets.UTF_8);// 付款金额 必填
        String subject = "充值";// 订单名称 必填
        String body = "用户充值";// 商品描述 选填

        // 生成支付页面信息
        alipayRequest.setBizContent("{\"out_trade_no\":\""+ outTradeNo +"\","
                + "\"total_amount\":\""+ totalAmount +"\","
                + "\"subject\":\""+ subject +"\","
                + "\"body\":\""+ body +"\","
                + "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"}");

        // 保存充值信息表
        Charge charge = new Charge();
        charge.setNo(no);
        charge.setUserId(userId);
        charge.setTime(LocalDateTime.now());
        charge.setState("支付中");
        chargeService.save(charge);

        // 返回请求结果 即支付页面代码
        String res = alipayClient.pageExecute(alipayRequest).getBody();

//        WebUtil.getResponse().setCharacterEncoding("utf8");
//        WebUtil.getResponse().setContentType("text/html;charset=utf8");
//        WebUtil.getResponse().getWriter().write(res);
//        WebUtil.getResponse().getWriter().flush();

        return res;
    }

    // 从支付宝异步回调中取得参数
    @Override
    public Map<String, String> convertParams(HttpServletRequest request) {
        Map<String, String[]> parameterMap = request.getParameterMap();// 从请求头中获取数据
        Set<Map.Entry<String, String[]>> entrySet = parameterMap.entrySet();// map2set
        Map<String,String> retMap = new HashMap<>();
        for (Map.Entry<String, String[]> entry : entrySet) {
            String name = entry.getKey();
            String[] values = entry.getValue();
            int valLen = values.length;
            if (valLen == 1) {
                retMap.put(name, values[0]);
            } else if (valLen > 1) {
                StringBuilder sb = new StringBuilder();
                for (String val : values) sb.append(",").append(val);
                retMap.put(name, sb.substring(1));
            } else {
                retMap.put(name, "");
            }
        }
        return retMap;
    }


    // 私有方法，向数据库中保存验证码
    private void saveCode(String email, String code) {
        // 通过redisTemplate获取操作redis的接口对象
        ValueOperations<String, String> operations = redisTemplate.opsForValue();
        operations.set(email, code, 10 * 60, TimeUnit.SECONDS);// 设置10分钟过期
    }

}
